use async_trait::async_trait;
use base_data::channel::EndReason;
use tools::{utils::current_data_time_with_zone, ResultNull};
use tracing::{debug, error, info};

use crate::{
	business::{
		bus_trait::Business,
		extra::{has_extra, Extra},
		inbound400::{
			bus_all_call::SaveCalleds,
			context::{BusinessState, InboundRunContext},
		},
	},
	db::persistence::InboundDBO,
	link_to_sip::{bridge_channel, record_sound},
};

///进入通话以后的操作
#[derive(Debug, Clone)]
pub struct Talk;

#[async_trait]
impl Business for Talk {
	type C = InboundRunContext;

	async fn start_call(&self, context: &mut InboundRunContext) -> ResultNull {
		debug!("开始启动通话处理..当前参数");

		if !context.is_bridge() {
			let Some(ref b_ch) = context.called_channel else {
				return Err("没有设置桥接的通道".into());
			};

			bridge_channel(context.caller_channel(), b_ch.id())?;
		}
		context.start_talk();

		// 开始计费,并且根据情况进行录音
		if has_extra(context.flag, Extra::Recording) {
			let rec_file_name = format!("{}.wav", &context.ch_call_id);
			record_sound(context.caller_channel(), &rec_file_name)?;
			context.rec_file_name = Some(rec_file_name);
		}

		Ok(())
	}

	async fn check(&self, context: &mut Self::C) -> ResultNull {
		let mut need_kill = false;
		//只有开始计费的才进行检查..检查费用是否足够
		if context.is_charging() {
			if let Some(b) = context.charge_time.as_ref() {
				let du = (current_data_time_with_zone().unix_timestamp() - b.unix_timestamp()) / 60;
				if du as f32 * context.talk_rate > context.money {
					//这是没钱了
					need_kill = true;
				}

				//一小时通话.需要挂断?
				if du > 60 {
					need_kill = true;
				}
			} else {
				//如果需要计费,但没有计费时间的.记录计费时间..正常这里不会进行到
				context.start_charge();
			}
		}

		if need_kill {
			context.business_state = BusinessState::End(EndReason::NotEnoughMoney);
			InboundDBO::record_and_charge(
				context,
				context.called_num(),
				&context.rec_file_name,
				EndReason::NotEnoughMoney,
				context.node_info.node_id,
				&context.node_info.dtfm,
				"",
			)
			.await?;
		}

		Ok(())
	}

	async fn kill_all(&self, context: &mut InboundRunContext) -> ResultNull {
		//收到挂断消息，同时清除全局呼叫记录
		SaveCalleds::remove_called(context.called_num()).await;
		Ok(())
	}

	async fn hangup(
		&self,
		context: &mut Self::C,
		channel_id: u32,
		_reason: EndReason,
	) -> ResultNull {
		info!("收到挂断消息");

		//收到挂断消息，同时清除全局呼叫记录
		SaveCalleds::remove_called(context.called_num()).await;

		//有时候送过来的挂断是之前呼叫进行的挂断。
		match context.called_channel().as_ref() {
			Some(b_ch) => {
				if !(channel_id == context.caller_id() || channel_id == b_ch.id()) {
					debug!("这个挂断消息非当前呼入呼出通道的,忽略");
					return Ok(());
				}
			}
			None => {
				if !(channel_id == context.caller_id()) {
					debug!("这个挂断消息非当前呼入通道的,忽略");
					return Ok(());
				}
			}
		}

		match context.business_state {
			BusinessState::Talk => {
				context.business_state = BusinessState::End(EndReason::TalkEnd);
			}
			BusinessState::End(_) => {
				debug!("收到挂断信息,业务已经进入结束状态,所以不再进行处理.");
			}
			BusinessState::Prepare
			| BusinessState::StartBusiness
			| BusinessState::WaitDtmf
			| BusinessState::StartCallOutbound => {
				error!("这里不应该收到这些消息...{:?}", &context.business_state)
			}
		}

		Ok(())
	}

	async fn closeing(&self, context: &mut InboundRunContext) -> ResultNull {
		//上传录音
		if let Some(name) = context.rec_file_name.as_ref() {
			InboundDBO::update_record_file(&context.target(), name).await?;
		}

		let r = match context.business_state {
			BusinessState::End(ref r) => r.clone(),
			_ => EndReason::UnknownValue,
		};

		//计费
		if context.is_charging() {
			InboundDBO::record_and_charge(
				context,
				context.called_num(),
				&context.rec_file_name,
				r,
				context.node_info.node_id,
				&context.node_info.dtfm,
				"",
			)
			.await?;
		}

		Ok(())
	}
}
